package com.streamhub.client;

import java.util.List;

import com.google.gwt.core.client.JavaScriptObject;

/**
 * The failover parameters for a connection to StreamHub.
 * 
 * @see StreamHubGWTAdapter#connect(FailoverParameters)
 */
public class FailoverParameters {
	private final List<String> servers;
	private final JavaScriptObject jsObject;
	private FailoverAlgorithm algorithm;
	private long initialReconnectDelayMillis;
	private long maxReconnectDelayMillis;
	private int maxReconnectAttempts;
	private boolean useExponentialBackOff;
	private double backOffMultiplier;

	/**
	 * Creates a new FailoverParameters object
	 * 
	 * @param servers	 A list of servers to connect to
	 */
	public FailoverParameters(List<String> servers) {
		this.jsObject = JavaScriptUtils.createObject();
		JavaScriptObject serversArray = JavaScriptUtils.toArray(servers);
		JavaScriptUtils.set(jsObject, "serverList", serversArray);
		this.servers = servers;
		this.setAlgorithm(FailoverAlgorithm.ORDERED);
		this.setInitialReconnectDelayMillis(1000);
		this.setMaxReconnectDelayMillis(-1);
		this.setMaxReconnectAttempts(-1);
		this.setUseExponentialBackOff(false);
		this.setBackOffMultiplier(1.0);
	}

	/**
	 * Gets the {@link FailoverAlgorithm}
	 * 
	 * @return	the failover algorithm
	 */
	public FailoverAlgorithm getAlgorithm() {
		return algorithm;
	}

	/**
	 * Sets the {@link FailoverAlgorithm}
	 * 
	 * @param algorithm	the failover algorithm to use
	 */
	public void setAlgorithm(FailoverAlgorithm algorithm) {
		this.algorithm = algorithm;
		JavaScriptUtils.set(jsObject, "failoverAlgorithm", algorithm.toString());
	}

	/**
	 * Gets the initial time in milliseconds to wait before attempting reconnection 
	 * 
	 * @return	the delay in milliseconds
	 */
	public long getInitialReconnectDelayMillis() {
		return initialReconnectDelayMillis;
	}
	
	/**
	 * Sets the initial time in milliseconds to wait before attempting reconnection 
	 * 
	 * @param initialReconnectDelayMillis	the delay in milliseconds
	 */
	public void setInitialReconnectDelayMillis(long initialReconnectDelayMillis) {
		this.initialReconnectDelayMillis = initialReconnectDelayMillis;
		JavaScriptUtils.set(jsObject, "initialReconnectDelayMillis", initialReconnectDelayMillis);
	}

	/**
	 * Gets the maximum amount of time in milliseconds to wait between reconnection attempts.  
	 * This value is only used when specifying exponential backoff, see {@link #setUseExponentialBackOff(boolean)}
	 * 
	 * @return	the max delay in milliseconds
	 */
	public long getMaxReconnectDelayMillis() {
		return maxReconnectDelayMillis;
	}

	/**
	 * Sets the maximum amount of time in milliseconds to wait between reconnection attempts.  
	 * This value is only used when specifying exponential backoff, see {@link #setUseExponentialBackOff(boolean)}
	 * 
	 * @param maxReconnectDelayMillis	the max delay in milliseconds
	 */
	public void setMaxReconnectDelayMillis(long maxReconnectDelayMillis) {
		this.maxReconnectDelayMillis = maxReconnectDelayMillis;
		JavaScriptUtils.set(jsObject, "maxReconnectDelayMillis", maxReconnectDelayMillis);
	}

	/**
	 * Gets the maximum number of failed reconnection attempts before giving up.  The default 
	 * is to infinitely attempt to reconnect until the connection is re-established.  This value 
	 * is reset upon a successful reconnection.
	 * 
	 * @return	the maximum number of attempts
	 */
	public int getMaxReconnectAttempts() {
		return maxReconnectAttempts;
	}

	/**
	 * Sets the maximum number of failed reconnection attempts before giving up.  The default 
	 * is to infinitely attempt to reconnect until the connection is re-established.  This value 
	 * is reset upon a successful reconnection.
	 * 
	 * @param maxReconnectAttempts	the maximum number of attempts
	 */
	public void setMaxReconnectAttempts(int maxReconnectAttempts) {
		this.maxReconnectAttempts = maxReconnectAttempts;
		JavaScriptUtils.set(jsObject, "maxReconnectAttempts", maxReconnectAttempts);
	}

	/**
	 * Returns <code>true</code> if exponential backoff is enabled, <code>false</code> 
	 * otherwise.
	 * 
	 * @return	<code>true</code> if exponential backoff is enabled, <code>false</code> otherwise	
	 */
	public boolean isUseExponentialBackOff() {
		return useExponentialBackOff;
	}

	/**
	 * Enables or disables exponential backoff.  This should be used in conjuction with 
	 * {@link #setBackOffMultiplier(double)}
	 * 
	 * @param useExponentialBackOff	<code>true</code to enable
	 */
	public void setUseExponentialBackOff(boolean useExponentialBackOff) {
		this.useExponentialBackOff = useExponentialBackOff;
		JavaScriptUtils.set(jsObject, "useExponentialBackOff", useExponentialBackOff);
	}

	/**
	 * Gets the multiplier used when exponential backoff is enabled via {@link #setUseExponentialBackOff(boolean)}.  
	 * The delay between each reconnect attempt will be multiplied by this number upon each failed 
	 * reconnection attempt.
	 * 
	 * @return	the multiplier
	 */
	public double getBackOffMultiplier() {
		return backOffMultiplier;
	}

	/**
	 * Sets the multiplier used when exponential backoff is enabled via {@link #setUseExponentialBackOff(boolean)}.  
	 * The delay between each reconnect attempt will be multiplied by this number upon each failed 
	 * reconnection attempt.
	 * 
	 * @return	the multiplier
	 */
	public void setBackOffMultiplier(double backOffMultiplier) {
		this.backOffMultiplier = backOffMultiplier;
		JavaScriptUtils.set(jsObject, "backOffMultiplier", backOffMultiplier);
	}

	/**
	 * Gets the list of server URLs to connect to
	 * 
	 * @return	a list containing the server URLs
	 */
	public List<String> getServers() {
		return servers;
	}
	
	JavaScriptObject getJavaScriptObject() {
		return jsObject;
	}
}
